package callgraph.process;

import callgraph.domain.JarConfig;
import callgraph.domain.ProfilingFilter;
import callgraph.dto.ExtendsClassMethodInfo;
import callgraph.dto.MethodAttribute;
import callgraph.dto.TmpNode4ExtendsClassMethod;
import callgraph.enumeration.CallTypeEnum;
import callgraph.util.JavaCGConstants;

import java.io.BufferedWriter;
import java.util.*;
import java.util.jar.JarFile;

class RecordExtendsClassMethod extends CallGraphProcessor {


    public RecordExtendsClassMethod(CallGraphProcessStorage storage, ProfilingFilter profilingFilter, JarConfig jarConfig, JarFile jarFile, BufferedWriter resultWriter) {
        super(storage, profilingFilter, jarConfig, jarFile, resultWriter);
    }

    @Override
    public boolean handle() {
        Set<String> topSuperClassNameSet = new HashSet<>();

        // 得到最顶层父类名称
        for (Map.Entry<String, ExtendsClassMethodInfo> extendsClassMethodInfoEntry : storage.getExtendsClassMethodInfoMap().entrySet()) {
            String className = extendsClassMethodInfoEntry.getKey();
            ExtendsClassMethodInfo extendsClassMethodInfo = extendsClassMethodInfoEntry.getValue();
            String superClassName = extendsClassMethodInfo.getSuperClassName();
            if (superClassName.startsWith("java.")) {
                topSuperClassNameSet.add(className);
            }
        }
        for (String topSuperClassName : topSuperClassNameSet) {
            // 处理一个顶层父类
            if (!handleOneTopSuperClass(topSuperClassName)) {
                return false;
            }
        }
        return true;
    }

    // 处理一个顶层父类
    private boolean handleOneTopSuperClass(String topSuperClassName) {
        System.out.println("处理一个顶层父类: " + topSuperClassName);
        List<TmpNode4ExtendsClassMethod> tmpNodeList = new ArrayList<>();
        int currentLevel = 0;

        // 初始化节点列表
        TmpNode4ExtendsClassMethod topNode = TmpNode4ExtendsClassMethod.genInstance(topSuperClassName, -1);
        tmpNodeList.add(topNode);

        // 开始循环
        while (true) {
            TmpNode4ExtendsClassMethod currentNode = tmpNodeList.get(currentLevel);
            List<String> childrenClassInfoList = storage.getChildrenClassInfoMap().get(currentNode.getSuperClassName());
            if (childrenClassInfoList == null) {
                System.err.println("### 未找到顶层父类: " + currentNode.getSuperClassName());
                return false;
            }

            int currentChildClassIndex = currentNode.getChildClassIndex() + 1;
            if (currentChildClassIndex >= childrenClassInfoList.size()) {
                if (currentLevel == 0) {
                    return true;
                }
                currentLevel--;
                continue;
            }

            // 处理当前的子类
            String childClassName = childrenClassInfoList.get(currentChildClassIndex);

            // 处理父类和子类的方法调用
            if (!handleSuperAndChildClass(currentNode.getSuperClassName(), childClassName)) {
                return false;
            }

            // 处理下一个子类
            currentNode.setChildClassIndex(currentChildClassIndex);

            List<String> nextChildClassList = storage.getChildrenClassInfoMap().get(childClassName);
            if (nextChildClassList == null) {
                // 当前的子类下没有子类
                continue;
            }

            // 当前的子类下有子类
            currentLevel++;

            if (currentLevel + 1 > tmpNodeList.size()) {
                TmpNode4ExtendsClassMethod nextNode = TmpNode4ExtendsClassMethod.genInstance(childClassName, -1);
                tmpNodeList.add(nextNode);
            } else {
                TmpNode4ExtendsClassMethod nextNode = tmpNodeList.get(currentLevel);
                nextNode.setSuperClassName(childClassName);
                nextNode.setChildClassIndex(-1);
            }
        }
    }

    // 处理父类和子类的方法调用
    private boolean handleSuperAndChildClass(String superClassName, String childClassName) {
        ExtendsClassMethodInfo superClassMethodInfo = storage.getExtendsClassMethodInfoMap().get(superClassName);
        if (superClassMethodInfo == null) {
            System.err.println("### 未找到父类信息: " + superClassName);
            return false;
        }

        ExtendsClassMethodInfo childClassMethodInfo = storage.getExtendsClassMethodInfoMap().get(childClassName);
        if (childClassMethodInfo == null) {
            System.err.println("### 未找到子类信息: " + childClassName);
            return false;
        }

        Map<String, MethodAttribute> superMethodAttributeMap = superClassMethodInfo.getMethodAttributeMap();
        Map<String, MethodAttribute> childMethodAttributeMap = childClassMethodInfo.getMethodAttributeMap();

        for (Map.Entry<String, MethodAttribute> superMethodAttributeEntry : superMethodAttributeMap.entrySet()) {
            String superMethodWithArgs = superMethodAttributeEntry.getKey();
            MethodAttribute superMethodAttribute = superMethodAttributeEntry.getValue();
            if (superMethodAttribute.isAbstractMethod()) {
                // 处理父类抽象方法
                childMethodAttributeMap.putIfAbsent(superMethodWithArgs, superMethodAttribute);
                // 添加父类调用子类的方法调用
                String superCallChildClassMethod = String.format("M:%d %s:%s (%s)%s:%s %d",
                        storage.getCallIdCounter().addAndGet(), superClassName, superMethodWithArgs,
                        CallTypeEnum.CTE_SCC.getType(), childClassName, superMethodWithArgs, JavaCGConstants.DEFAULT_LINE_NUMBER);
                // TODO: 2021/11/14 write record
                writeResult(superCallChildClassMethod);
                writeResult(" " + jarNum);
                writeResult(JavaCGConstants.NEW_LINE);
                continue;
            }
            if (superMethodAttribute.isPublicMethod() || superMethodAttribute.isProtectedMethod()) {
                // 父类的public/protected且非抽象方法
                if (childMethodAttributeMap.get(superMethodWithArgs) != null) {
                    continue;
                }
                Set<String> childCalleeMethodWithArgsSet = storage.getCalleeMethodMapGlobal().get(childClassName);
                if (!childClassMethodInfo.isAbstractClass() &&
                        (childCalleeMethodWithArgsSet == null || !childCalleeMethodWithArgsSet.contains(superMethodWithArgs))) {
                    continue;
                }

                childMethodAttributeMap.put(superMethodWithArgs, superMethodAttribute);

                // 添加子类调用父类方法
                String childCallSuperClassMethod = String.format("M:%d %s:%s (%s)%s:%s %d", storage.getCallIdCounter().addAndGet(),
                        childClassName, superMethodWithArgs,
                        CallTypeEnum.CTE_CCS.getType(), superClassName, superMethodWithArgs, JavaCGConstants.DEFAULT_LINE_NUMBER);
                // TODO: 2021/11/14 write record
                writeResult(childCallSuperClassMethod);
                writeResult(" " + jarNum);
                writeResult(JavaCGConstants.NEW_LINE);
            }
        }
        return true;
    }


    @Override
    public CallGraphProcessor next() {
        return new RecordInterfaceCallClassMethod(storage, profilingFilter, jarConfig, jarFile, resultWriter);
    }
}
